﻿using FreeSql;
using System;
using System.Linq;
using TK.FreeSqlExtension;
using TK.FreeSqlExtension.Repository;

namespace Microsoft.Extensions.DependencyInjection
{
    /// <summary>
    /// ServiceExtensions
    /// </summary>
    public static class ServiceExtensions
    {
        /// <summary>
        /// 添加基于freesql的orm服务；默认注册第一个freesql; 可通过IFreeSqlFactory获取不同freesql
        /// 切记：自行引用freesql对应数据库驱动
        /// </summary>
        public static IServiceCollection AddFreeSqlOrm(this IServiceCollection services, Action<OrmBuilderConfig> action)
        {
            OrmBuilderConfig builderConfig = new OrmBuilderConfig();
            action?.Invoke(builderConfig);
            if (builderConfig.ConnectionList != null && builderConfig.ConnectionList.Any())
            {
                var firstDbKey = builderConfig.ConnectionList[0].DbKey;
                foreach (DbConnectionConfig conn in builderConfig.ConnectionList)
                {
                    if (string.IsNullOrWhiteSpace(conn.DbKey) || string.IsNullOrWhiteSpace(conn.Connection))
                    {
                        throw new ArgumentNullException("DbKey或Connection参数为空");
                    }

                    var freeSqlBuilder = new FreeSqlBuilder().UseConnectionString(conn.DataType, conn.Connection);
                    freeSqlBuilder.UseAutoSyncStructure(false);
                    freeSqlBuilder.UseNameConvert(FreeSql.Internal.NameConvertType.None);
                    if (builderConfig.ConnectionList.IndexOf(conn) == 0 && builderConfig.SlaveConnectionList != null && builderConfig.SlaveConnectionList.Any())
                    {
                        freeSqlBuilder.UseSlave(builderConfig.SlaveConnectionList);
                    }

                    builderConfig.BuilderAction?.Invoke(freeSqlBuilder);
                    IFreeSql freeSql = freeSqlBuilder.Build();
                    builderConfig.GlobalFilterAction?.Invoke(conn.DataType, freeSql.GlobalFilter);
                    builderConfig.CodeFirstAction?.Invoke(conn.DataType, freeSql.CodeFirst);
                    builderConfig.AopAction?.Invoke(conn.DataType, freeSql.Aop);
                    services.AddSingleton<IFreeSqlClient>(sp => new FreeSqlClient { DbKey = conn.DbKey, FreeSql = freeSql });
                }

                services.AddSingleton<IFreeSqlFactory, FreeSqlFactory>();
                AddRepository(services);
            }

            return services;
        }

        /// <summary>
        /// 添加基于freesql的orm服务；默认注册第一个freesql; 可通过IFreeSq扩展函数ChangeDatabaseByKey、Change切换没同数据库链接;  
        /// 可通过请求中间件(使用ChangeDatabaseByKey)动态切换数据库及相关更改相关表名。
        /// 切记：自行引用freesql对应数据库驱动
        /// </summary>
        public static IServiceCollection AddMultiFreeSql(this IServiceCollection services, Action<OrmBuilderConfig> action)
        {
            OrmBuilderConfig builderConfig = new OrmBuilderConfig();
            action?.Invoke(builderConfig);
            if (builderConfig.ConnectionList != null && builderConfig.ConnectionList.Any())
            {
                var multiFsql = new MultiFreeSql(TimeSpan.FromHours(2));
                foreach (DbConnectionConfig conn in builderConfig.ConnectionList)
                {
                    if (string.IsNullOrWhiteSpace(conn.DbKey) || string.IsNullOrWhiteSpace(conn.Connection))
                    {
                        throw new ArgumentNullException("DbKey或Connection参数为空");
                    }

                    multiFsql.Register(conn.DbKey, () =>
                    {
                        var freeSqlBuilder = new FreeSqlBuilder().UseConnectionString(conn.DataType, conn.Connection);
                        freeSqlBuilder.UseAutoSyncStructure(false);
                        freeSqlBuilder.UseNameConvert(FreeSql.Internal.NameConvertType.None);
                        if (builderConfig.ConnectionList.IndexOf(conn) == 0 && builderConfig.SlaveConnectionList != null && builderConfig.SlaveConnectionList.Any())
                        {
                            freeSqlBuilder.UseSlave(builderConfig.SlaveConnectionList);
                        }

                        builderConfig.BuilderAction?.Invoke(freeSqlBuilder);
                        var freeSql = freeSqlBuilder.Build();
                        builderConfig.GlobalFilterAction?.Invoke(conn.DataType, freeSql.GlobalFilter);
                        builderConfig.CodeFirstAction?.Invoke(conn.DataType, freeSql.CodeFirst);
                        builderConfig.AopAction?.Invoke(conn.DataType, freeSql.Aop);

                        return freeSql;
                    });
                }

                services.AddSingleton<IFreeSql>(provider => multiFsql);
                AddRepository1(services);
            }

            return services;
        }

        #region private methods
        /// <summary>
        /// AddRepository
        /// </summary>
        private static void AddRepository(IServiceCollection services)
        {
            services.AddScoped<UnitOfWorkManager, MyUnitOfWorkManager>();
            services.AddScoped<IUnitOfWork>(provider =>
            {
                return provider.GetService<IFreeSqlFactory>().GetFreeSql().CreateUnitOfWork();
            });
            services.AddScoped(typeof(IBaseRepository<>), typeof(MyGuidRepository<>));
            services.AddScoped(typeof(BaseRepository<>), typeof(MyGuidRepository<>));
            services.AddScoped(typeof(IBaseRepository<,>), typeof(MyDefaultRepository<,>));
            services.AddScoped(typeof(BaseRepository<,>), typeof(MyDefaultRepository<,>));
            System.Reflection.Assembly[] assemblyArray = AppDomain.CurrentDomain.GetAssemblies();
            if (assemblyArray?.Any() == true)
            {
                foreach (System.Reflection.Assembly assembly in assemblyArray)
                {
                    foreach (Type type in assembly.GetExportedTypes().Where(a => typeof(IBaseRepository).IsAssignableFrom(a) &&
                    a.IsPublic && !a.IsInterface && !a.IsAbstract && !a.IsGenericType))
                    {
                        services.AddScoped(type);
                    }
                }
            }
        }

        /// <summary>
        /// AddRepository1
        /// </summary>
        private static void AddRepository1(IServiceCollection services)
        {
            services.AddScoped<UnitOfWorkManager>();
            services.AddScoped<IUnitOfWork>(provider =>
            {
                return provider.GetService<IFreeSql>().CreateUnitOfWork();
            });
            services.AddScoped(typeof(IBaseRepository<>), typeof(GuidRepository<>));
            services.AddScoped(typeof(BaseRepository<>), typeof(GuidRepository<>));
            services.AddScoped(typeof(IBaseRepository<,>), typeof(DefaultRepository<,>));
            services.AddScoped(typeof(BaseRepository<,>), typeof(DefaultRepository<,>));
            System.Reflection.Assembly[] assemblyArray = AppDomain.CurrentDomain.GetAssemblies();
            if (assemblyArray?.Any() == true)
            {
                foreach (System.Reflection.Assembly assembly in assemblyArray)
                {
                    foreach (Type type in assembly.GetExportedTypes().Where(a => typeof(IBaseRepository).IsAssignableFrom(a) &&
                    a.IsPublic && !a.IsInterface && !a.IsAbstract && !a.IsGenericType))
                    {
                        services.AddScoped(type);
                    }
                }
            }
        }

        #endregion
    }
}
